unit RadarMgrUnit;

interface

uses Windows, Classes, SysUtils, SPComm, Dialogs;

type

  CheckZone = (czSame, czOpposite, czBiDirectional, czUnknown);

  CutOff = (coLow, coHigh, coUnknown);

  SwitchParam = (spOff, spOn, spUnknown);

  CheckMode = (cmStationary, cmMoveing, cmUnknown);

  SpeedType = (stTarget, stFaster, stLocked, stPatrol);

  // other type for command id table
  ConfigType = (ctMode = 1, ctZone, ctSensitivity, ctOSSensitivity,
    ctSameSensitivity, ctFactorySensitivity, ctStationarySensitivity,
    ctPatrolSensitivity, ctForkEnable = 47);

  TSpeedChangedEvent = procedure(AType: SpeedType) of Object;
  TConfigChangedEvent = procedure(AType: ConfigType) of Object;

  TRadarMgr = class
  private
    DataLen: Integer;
    ReceiveData: Array [0 .. 20] of Byte;
    FComm: TComm;
    FStarting: Boolean;
    FCanControl: Boolean;
    InitConfiging: Boolean;
    BeginInitQuery: Boolean;

    FOnSpeedChanged: TSpeedChangedEvent;
    FOnConfigChanged: TConfigChangedEvent;
    FOnControlReady: TNotifyEvent;

    FLock: TRTLCriticalSection;

    procedure SpCommReceiveData(Sender: TObject; Buffer: PAnsiChar;
      BufferLength: Word);
    procedure DataReceive(AData: Byte);
    procedure OperData();
    procedure OperConfigData();
    function CheckData(): Boolean;
    function CheckConfigData(): Boolean;

    procedure SendData(const CommandId, Value: Byte);
    procedure ChangeConfig(AType: ConfigType; const Value: Byte); overload;

    procedure SpSendDataEmpty(Sender: TObject);

    procedure QueryAllConfig();
    procedure SetFactorySensitivity(const Value: Integer);
  protected
    FZone: CheckZone;
    FMode: CheckMode;
    FForkEnable: SwitchParam;
    FSensitivity,
    FOSSensitivity, FSameSensitivity, FFactorySensitivity: Integer;
    FPatrolCutOff, FStationaryCutOff: CutOff;

    FTargetSpeed, FFasterSpeed, FLockedSpeed, FPatrolSpeed: Integer;

    procedure SetMode(const Value: CheckMode);
    procedure SetZone(const Value: CheckZone);

    procedure SetOSSensitivity(const Value: Integer);
    procedure SetPatrolCutOff(const Value: CutOff);
    procedure SetSameSensitivity(const Value: Integer);
    procedure SetSensitivity(const Value: Integer);
    procedure SetStationaryCutOff(const Value: CutOff);
    procedure SetForkEnable(const Value: SwitchParam);
    procedure QueryConfig(AType: ConfigType);
  public
    procedure Connect(ComName: String; BaudRate: Integer);
    procedure Close();

    // get config max min value
    property Zone: CheckZone read FZone write SetZone;
    property Mode: CheckMode read FMode write SetMode;
    property Sensitivity: Integer read FSensitivity write SetSensitivity;
    property OSSensitivity: Integer read FOSSensitivity write SetOSSensitivity;
    property SameSensitivity: Integer read FSameSensitivity
      write SetSameSensitivity;
    property PatrolCutOff: CutOff read FPatrolCutOff write SetPatrolCutOff;
    property StationaryCutOff: CutOff read FStationaryCutOff
      write SetStationaryCutOff;
    property ForkEnable: SwitchParam read FForkEnable write SetForkEnable;
    property FactorySensitivity: Integer read FFactorySensitivity
      write SetFactorySensitivity;

    property TargetSpeed: Integer read FTargetSpeed;
    property FasterSpeed: Integer read FFasterSpeed;
    property LockedSpeed: Integer read FLockedSpeed;
    property PatrolSpeed: Integer read FPatrolSpeed;

    property CanControl: Boolean read FCanControl;

    property OnSpeedChanged: TSpeedChangedEvent read FOnSpeedChanged
      write FOnSpeedChanged;
    property OnConfigChanged: TConfigChangedEvent read FOnConfigChanged
      write FOnConfigChanged;
    property OnControlReady: TNotifyEvent read FOnControlReady
      write FOnControlReady;

    constructor Create();
    destructor Destroy(); override;
  end;

implementation

uses TLoggerUnit;

{ TRadarMgr }

procedure TRadarMgr.ChangeConfig(AType: ConfigType; const Value: Byte);
begin
  SendData($80 + Byte(AType), Value);
end;

function TRadarMgr.CheckConfigData: Boolean;
var
  I: Integer;
  SumValue, SingValue: LongWord;
begin
  SumValue := 0;
  for I := 0 to 4 do
  begin
    if I < 4 then
    begin
      SingValue := ReceiveData[I * 2 + 1];
      SingValue := SingValue shl 8;
      SumValue := SumValue + (SingValue or ReceiveData[I * 2]);
    end
    else
      SumValue := SumValue + ReceiveData[I * 2];
  end;
  Result := ((SumValue and $FF) = ReceiveData[9]) and
    (((SumValue shr 8) and $FF) = ReceiveData[10]);
end;

function TRadarMgr.CheckData: Boolean;
var
  I: Integer;
  SumValue, SingValue: LongWord;
begin
  SumValue := 0;
  for I := 0 to 9 do
  begin
    if I < 9 then
    begin
      SingValue := ReceiveData[I * 2 + 1];
      SingValue := SingValue shl 8;
      SumValue := SumValue + (SingValue or ReceiveData[I * 2]);
    end
    else
      SumValue := SumValue + ReceiveData[I * 2];
  end;
  Result := ((SumValue and $FF) = ReceiveData[19]) and
    (((SumValue shr 8) and $FF) = ReceiveData[20]);
end;

procedure TRadarMgr.Close;
begin
  FComm.StopComm;
end;

constructor TRadarMgr.Create;
begin
  FComm := TComm.Create(nil);
  FComm.OnReceiveData := SpCommReceiveData;
  FComm.OnSendDataEmpty := SpSendDataEmpty;
  FStarting := False;
  InitializeCriticalSection(FLock);
end;

destructor TRadarMgr.Destroy;
begin
  if FStarting then
    FComm.StopComm;
  FreeAndNil(FComm);
  DeleteCriticalSection(FLock);
  inherited;
end;

procedure TRadarMgr.Connect(ComName: String; BaudRate: Integer);
begin
  FCanControl := False;

  if FStarting then
    FComm.StopComm;
  DataLen := 0;
  FTargetSpeed := -1;
  FFasterSpeed := -1;
  FLockedSpeed := -1;
  FPatrolSpeed := -1;
  FFactorySensitivity := -1;
  FMode := cmUnknown;
  FZone := czUnknown;
  FSensitivity := -1;
  FOSSensitivity := -1;
  FSameSensitivity := -1;
  FPatrolCutOff := coUnknown;
  FStationaryCutOff := coUnknown;
  FForkEnable := spUnknown;

  FComm.CommName := ComName;
  FComm.BaudRate := BaudRate;
  FComm.StartComm;
  FStarting := True;
  InitConfiging := True;
  BeginInitQuery := False;

end;

procedure TRadarMgr.OperConfigData;
var
  NewValue: LongWord;
  NewCutOffValue: CutOff;
  NewZone: CheckZone;
  NewMode: CheckMode;
  NewSwitchParam: SwitchParam;
  RConfigType: ConfigType;
begin
  RConfigType := ConfigType(ReceiveData[6]);
  TLogger.GetInstance.Debug('OperConfigData:' + IntToStr(Integer(RConfigType)));
  case RConfigType of
    ctMode:
      begin
        NewMode := CheckMode(ReceiveData[8]);
        if FMode <> NewMode then
        begin
          FMode := NewMode;
          if Assigned(FOnConfigChanged) then
            FOnConfigChanged(RConfigType);
        end;
      end;
    ctZone:
      begin
        NewZone := CheckZone(ReceiveData[8]);
        if FZone <> NewZone then
        begin
          FZone := NewZone;
          if Assigned(FOnConfigChanged) then
            FOnConfigChanged(RConfigType);
        end;
      end;
    ctSensitivity:
      begin
        NewValue := ReceiveData[8];
        if Integer(NewValue) <> FSensitivity then
        begin
          FSensitivity := NewValue;
          if Assigned(FOnConfigChanged) then
            FOnConfigChanged(RConfigType);
        end;
      end;
    ctFactorySensitivity:
      begin
        NewValue := ReceiveData[8];
        if Integer(NewValue) <> FFactorySensitivity then
        begin
          FFactorySensitivity := NewValue;
          if Assigned(FOnConfigChanged) then
            FOnConfigChanged(RConfigType);
        end;
      end;
    ctOSSensitivity:
      begin
        NewValue := ReceiveData[8];
        if Integer(NewValue) <> FOSSensitivity then
        begin
          FOSSensitivity := NewValue;
          if Assigned(FOnConfigChanged) then
            FOnConfigChanged(RConfigType);
        end;
      end;
    ctSameSensitivity:
      begin
        NewValue := ReceiveData[8];
        if Integer(NewValue) <> FSameSensitivity then
        begin
          FSameSensitivity := NewValue;
          if Assigned(FOnConfigChanged) then
            FOnConfigChanged(RConfigType);
        end;
      end;
    ctStationarySensitivity:
      begin
        NewCutOffValue := CutOff(ReceiveData[8]);
        if NewCutOffValue <> FStationaryCutOff then
        begin
          FStationaryCutOff := NewCutOffValue;
          if Assigned(FOnConfigChanged) then
            FOnConfigChanged(RConfigType);
        end;
      end;
    ctPatrolSensitivity:
      begin
        NewCutOffValue := CutOff(ReceiveData[8]);
        if NewCutOffValue <> FPatrolCutOff then
        begin
          FPatrolCutOff := NewCutOffValue;
          if Assigned(FOnConfigChanged) then
            FOnConfigChanged(RConfigType);
        end;
      end;
    ctForkEnable:
      begin
        NewSwitchParam := SwitchParam(ReceiveData[8]);
        if NewSwitchParam <> FForkEnable then
        begin
          FForkEnable := NewSwitchParam;
          if Assigned(FOnConfigChanged) then
            FOnConfigChanged(RConfigType);
        end;
      end
  else
    ; // other config
  end;
  QueryAllConfig;
end;

procedure TRadarMgr.OperData;
var
  NewTargetSpeed, NewFasterSpeed, NewLockedSpeed, NewPatrolSpeed: LongWord;
  NewZone: CheckZone;
  NewMode: CheckMode;
begin
  NewMode := CheckMode((ReceiveData[18] and 1) = 1);
  //TLogger.GetInstance.Debug('OperData Mode:' + IntToStr(Integer(NewMode)));
  if FMode <> NewMode then
  begin
    FMode := NewMode;
    if Assigned(FOnConfigChanged) then
      FOnConfigChanged(ctMode);
  end;
  NewZone := CheckZone((ReceiveData[18] shr 1) and $3);
  if NewZone <> FZone then
  begin
    FZone := NewZone;
    if Assigned(FOnConfigChanged) then
      FOnConfigChanged(ctZone);
  end;

  NewTargetSpeed := ReceiveData[9];
  NewTargetSpeed := NewTargetSpeed shl 8;
  NewTargetSpeed := NewTargetSpeed or ReceiveData[8];
  TLogger.GetInstance.Debug('OperData TargetSpeed:' + IntToStr(NewTargetSpeed));
  if NewTargetSpeed <> FTargetSpeed then
  begin
    FTargetSpeed := NewTargetSpeed;
    if Assigned(FOnSpeedChanged) then
      FOnSpeedChanged(stTarget);
  end;

  NewFasterSpeed := ReceiveData[11];
  NewFasterSpeed := NewFasterSpeed shl 8;
  NewFasterSpeed := NewFasterSpeed or ReceiveData[10];
  if NewFasterSpeed <> FFasterSpeed then
  begin
    FFasterSpeed := NewFasterSpeed;
    if Assigned(FOnSpeedChanged) then
      FOnSpeedChanged(stFaster);
  end;

  NewLockedSpeed := ReceiveData[13];
  NewLockedSpeed := NewLockedSpeed shl 8;
  NewLockedSpeed := NewLockedSpeed or ReceiveData[12];
  if NewLockedSpeed <> FLockedSpeed then
  begin
    FLockedSpeed := NewLockedSpeed;
    if Assigned(FOnSpeedChanged) then
      FOnSpeedChanged(stLocked);
  end;
  NewPatrolSpeed := ReceiveData[15];
  NewPatrolSpeed := NewPatrolSpeed shl 8;
  NewPatrolSpeed := NewPatrolSpeed or ReceiveData[14];
  if NewPatrolSpeed <> FPatrolSpeed then
  begin
    FPatrolSpeed := NewPatrolSpeed;
    if Assigned(FOnSpeedChanged) then
      FOnSpeedChanged(stPatrol);
  end;
end;

procedure TRadarMgr.QueryAllConfig;
var
  AllComplete: Boolean;
begin
  if InitConfiging then
  begin
    AllComplete := True;
    if FSensitivity < 0 then
    begin
      QueryConfig(ctSensitivity);
      AllComplete := False;
    end
    else if FOSSensitivity < 0 then
    begin
      QueryConfig(ctOSSensitivity);
      AllComplete := False;
    end
    else if FSameSensitivity < 0 then
    begin
      QueryConfig(ctSameSensitivity);
      AllComplete := False;
    end
    else if FStationaryCutOff = coUnknown then
    begin
      QueryConfig(ctStationarySensitivity);
      AllComplete := False;
    end
    else if FPatrolCutOff = coUnknown then
    begin
      QueryConfig(ctPatrolSensitivity);
      AllComplete := False;
    end
    else if FForkEnable = spUnknown then
    begin
      QueryConfig(ctForkEnable);
      AllComplete := False;
    end
    else if FFactorySensitivity < 0 then
    begin
      QueryConfig(ctFactorySensitivity);
      AllComplete := False;
    end;
    InitConfiging := not AllComplete;
    if AllComplete and Assigned(FOnControlReady) then
    begin
      FOnControlReady(Self);
      TLogger.GetInstance.Debug('All config is ready');
    end;
  end;
end;

procedure TRadarMgr.QueryConfig(AType: ConfigType);
begin
  TLogger.GetInstance.Debug('call QueryConfig:' + IntToStr(Integer(AType)));
  SendData(Byte(AType), $00);
end;

procedure TRadarMgr.DataReceive(AData: Byte);
var
  I: Integer;
begin
  if DataLen >= 21 then
  begin
    for I := 0 to 19 do
      ReceiveData[I] := ReceiveData[I + 1];
    DataLen := 20;
  end;
  ReceiveData[DataLen] := AData;
  DataLen := DataLen + 1;
  if (DataLen >= 11) and (ReceiveData[0] = $EF) and (ReceiveData[1] = $01) and
    (ReceiveData[2] = $02) and (ReceiveData[3] = $00) and CheckConfigData() then
  begin
    OperConfigData();
    DataLen := DataLen - 11;
    for I := 0 to DataLen - 1 do
      ReceiveData[I] := ReceiveData[I + 11];
  end;
  if (DataLen >= 21) and (ReceiveData[0] = $EF) and (ReceiveData[1] = $FF) and
    (ReceiveData[2] = $02) and (ReceiveData[3] = $01) and CheckData() then
  begin
    OperData;
    DataLen := 0;
  end;

end;

procedure TRadarMgr.SendData(const CommandId, Value: Byte);
var
  MSendData: array [0 .. 10] of AnsiChar;
  CheckData: LongWord;
  SumData: LongWord;
  I: Integer;
begin
//  if not FComm.SendDataEmpty then
//    raise Exception.Create('Data is Sending');
  MSendData[0] := AnsiChar($EF);
  MSendData[1] := AnsiChar($02);
  MSendData[2] := AnsiChar($01);
  MSendData[3] := AnsiChar($00);
  MSendData[4] := AnsiChar($03);
  MSendData[5] := AnsiChar($00);
  MSendData[6] := AnsiChar(CommandId);
  MSendData[7] := AnsiChar($00);
  MSendData[8] := AnsiChar(Value);

  SumData := 0;
  for I := 0 to 3 do
  begin
    CheckData := Byte(MSendData[I * 2 + 1]);
    CheckData := (CheckData shl 8) or Byte(MSendData[I * 2]);
    SumData := SumData + CheckData;
  end;
  SumData := SumData + Byte(MSendData[8]);
  MSendData[9] := AnsiChar(SumData and $FF);
  MSendData[10] := AnsiChar((SumData shr 8) and $FF);
  FComm.WriteCommData(MSendData, 11);
  FCanControl := False;
  TLogger.GetInstance.Debug('SendData:' + IntToStr(CommandId));
end;

procedure TRadarMgr.SetFactorySensitivity(const Value: Integer);
begin
  ChangeConfig(ctFactorySensitivity, Byte(Value));
end;

procedure TRadarMgr.SetForkEnable(const Value: SwitchParam);
begin
  // FForkEnable := Value;
  ChangeConfig(ctForkEnable, Byte(Value));
end;

procedure TRadarMgr.SetMode(const Value: CheckMode);
begin
  // FMode := Value;
  ChangeConfig(ctMode, Byte(Value));
end;

procedure TRadarMgr.SetOSSensitivity(const Value: Integer);
begin
  // FOSSensitivity := Value;
  ChangeConfig(ctOSSensitivity, Byte(Value));
end;

procedure TRadarMgr.SetPatrolCutOff(const Value: CutOff);
begin
  // FPatrolCutOff := Value;
  ChangeConfig(ctPatrolSensitivity, Byte(Value));
end;

procedure TRadarMgr.SetSameSensitivity(const Value: Integer);
begin
  // FSameSensitivity := Value;
  ChangeConfig(ctSameSensitivity, Byte(Value));
end;

procedure TRadarMgr.SetSensitivity(const Value: Integer);
begin
  // FSensitivity := Value;
  ChangeConfig(ctSensitivity, Byte(Value));
end;

procedure TRadarMgr.SetStationaryCutOff(const Value: CutOff);
begin
  // FStationaryCutOff := Value;
  ChangeConfig(ctStationarySensitivity, Byte(Value));
end;

procedure TRadarMgr.SetZone(const Value: CheckZone);
begin
  // FZone := Value;
  ChangeConfig(ctZone, Byte(Value));
end;

procedure TRadarMgr.SpCommReceiveData(Sender: TObject; Buffer: PAnsiChar;
  BufferLength: Word);
var
  I: Integer;
begin
  EnterCriticalSection(FLock);
  try
    for I := 0 to BufferLength - 1 do
    begin
      DataReceive(Byte(Buffer[I]));
    end;
  finally
    LeaveCriticalSection(FLock);
  end;
  if InitConfiging and not BeginInitQuery then
  begin
    QueryAllConfig();
    BeginInitQuery := True;
  end;
end;

procedure TRadarMgr.SpSendDataEmpty(Sender: TObject);
begin
  TLogger.GetInstance.Debug('call SpSendDataEmpty:' +
    BoolToStr(FComm.SendDataEmpty));
  if not InitConfiging then
  begin
    FCanControl := FComm.SendDataEmpty;
    if FCanControl and Assigned(FOnControlReady) then
      FOnControlReady(Self);
  end
  else
    FCanControl := False;
end;

initialization

finalization

TLogger.FreeInstances;

end.
